iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0

前幾天已經看過,Scheduler 會替 Pod 選擇 Worker Node,而 kubelet、Container Runtime、CNI 與 CSI 會在該 Node 上把它真正準備起來。不過到這裡還有一個容易混淆的地方,
我們常說「部署一個 container」,但 Kubernetes 實際管理與派送的最小單位是 Pod

Pod 可以只有一個 container,也可以包含數個 container,彼此之間可能有依賴關係。今天主要針對 Pod 以及不同情境類型的 Container 來說明,Pod 裡有哪些 container、它們什麼時候啟動與結束,以及為什麼有時候明明是自己的應用沒改,Pod 卻會因為另一個 container 而無法啟動。

Pod:Kubernetes 管理的最小可部署單位

Pod 不等於 Container,而一個 Pod 內也不一定只有一個 Container

Kubernetes 將 Pod 定義為最小可部署單位,一個 Pod 會把一個或多個 container 放在同一個執行環境:它們會一起被排程到同一台 Worker Node、共享 Pod IP 與網路空間,也可以掛載同一個 Volume。

https://ithelp.ithome.com.tw/upload/images/20260922/20124323TCcdi3jVqu.png

以本次微服務架構的後端站台為例,api-a 可以獨立部署成一個 Pod,Pod 內主要放執行 Kestrel 的 ASP.NET Core WebAPI container。而 api-b 不需要放進同一個 Pod,因為它是另一個可獨立部署、更新與擴展(Scaling)的服務,應該各自放在不同的 Pod,再透過 Service 以網路互相呼叫。

只有在多個 container 必須接續、同時工作時,才適合放進同一個 Pod。例如應用程式需要搭配 Log 搜集器/轉發器一起運作,或啟動前必須先完成某個一次性的初始化工作,這些 container 可以隨 Pod 一起被排程,並共享該 Pod 的網路環境(可以使用 localhost 去互相溝通);若掛載同一個 Volume,也能共享檔案。

同一個 Pod 共享與不共享的東西

同一 Pod 內的 container 至少共享以下幾件事:

  • 網路:共用同一個 Pod IP、network namespace 與 Port 空間,container 與 container 之間能以 localhost 與不同 Port Number 互相連線。
  • Volume:若同時掛載同一個 Volume,就能以檔案交換資料。

但 container 並不會因此自動共享所有東西。例如每個 container 仍有自己的 image、process、environment variable、resource request/limit 與預設檔案系統。排障時也要看清楚是哪一個 container 失敗,而不是只看 Pod 整體狀態。

Pod 裡常見的三種 container 角色

類型 何時運作 常見用途
App Container Pod 正常服務期間持續運作 Web API、前端網站、背景處理程序
Init Container 啟動 App Container 前,依宣告順序逐一執行,處理完成後此容器將下架 等待必要條件、產生一次性檔案、資料初始化
Sidecar Container 與 App Container 協作,在 Pod 存活期間提供輔助功能;實際啟動與停止順序取決於採用傳統或 native 配置 Proxy、log 收集、監控、同步檔案

App Container:提供業務功能的容器

例如 ASP.NET Core API、Nginx 或背景 Worker。它才是處理 request、執行商業邏輯、回傳資料的主要容器。

一個 Pod 雖然可以有多個 App Container,但如果它們沒有明確的共用資源或需要有相同生命週期的需求,基本上拆成不同 Pod 通常更容易去達到 Auto Scaling、debug 與 release。

Init Container:先啟動並完成任務,才輪到 App Container 啟動

Init Container 依 Deployment 的 .spec.template.spec.initContainers 宣告順序執行,而且前一個 Init Container 成功結束後,才會啟動下一個。所有 Init Container 都成功後,kubelet 才會開始啟動 App Container。

例如,一個 Init Container 可以確認某個必要檔案存在、把範本設定檔轉成應用實際需要的設定檔,或等待外部依賴服務是可連線的狀態。

它不適合承接長時間服務 request,如果它一直無法成功結束,Pod 就會停在初始化(init)階段。

Sidecar:App Container 的輔助角色

Sidecar 是一種「協作角色」,它的工作是補足 App Container 的能力,或讓 App Container 的職責更單純,例如:

  • Proxy 代替應用處理連線、加密或流量治理(e.g. Envoy Proxy)。
  • Log 收集器讀取 App Container 內的 log 檔案,並送往集中式平台。
  • 監控應用程式。

Sidecar 是協作角色,常見有兩種配置方式,分別是傳統 Sidecar 與 native Sidecar。

為了和 Kubernetes 的 native Sidecar 區分(也就是用 Init Container 作為 Sidecar 的方式),我將放在 containers 的 Sidecar 稱為「傳統 Sidecar」。

傳統 Sidecar:與 App Container 同層

傳統 Sidecar 寫在 .spec.template.spec.containers,與 App Container 同層。

從 Kubernetes API 的角度來看,兩者都是一般的 App container。

像是 Istio injection 所加入的 istio-proxy,就是常見例子。所有 Init Container 成功後,App Container 與傳統 Sidecar 才會開始,但 Kubernetes 預設不保證它們彼此的啟動或停止先後順序。

native Sidecar:以 initContainers 宣告、持續執行

其實我也是撰寫這篇時才發現原來也有這種方式可以達成 Sidecar。

Kubernetes 在 v1.32 版本提供了這種方式來讓 Init Container 也能當作 Sidecar 來用。它寫在 .spec.template.spec.initContainers,並在該 container 設定 restartPolicy: Always,這種使用方式不會像一般 Init Container 一樣完成後結束。

native Sidecar 會依 initContainers 的宣告順序先啟動,並持續運作到 Pod 終止;Kubernetes 會在主要 App Container 都停止後,才關閉 native Sidecar。

以下以 Deployment 的 Pod template 為例,只保留與兩種 Sidecar 配置有關的區塊。兩者最大的差異,就是 Sidecar 所在的欄位與 native Sidecar 額外設定的 restartPolicy: Always

傳統 Sidecar:兩個 container 都放在 containers

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
        - name: api
          image: example/api:1.0
        - name: proxy
          image: example/proxy:1.0

native Sidecar:Sidecar 放在 initContainers,但會持續執行

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      initContainers:
        - name: proxy
          image: example/proxy:1.0
          restartPolicy: Always
      containers:
        - name: api
          image: example/api:1.0

以下比較傳統 Sidecar 與 native Sidecar 的 lifecycle:

https://ithelp.ithome.com.tw/upload/images/20260922/20124323CaZOxcDVQX.png

一個 Pod 的生命週期

把 Day 6、Day 7 的流程與今天的 container 角色接起來,可以得到下列流程:

https://ithelp.ithome.com.tw/upload/images/20260922/20124323iWlACuGXwH.png

  1. Deployment 或其他 Workload 建立 Pod 的宣告,Scheduler 將尚未分派的 Pod 指派給一台 Worker Node。
  2. 該 Node 的 kubelet 透過 Runtime 準備 Pod Sandbox、image 與網路。
  3. Init Container 依序執行。任一個 Init Container 啟動失敗,App Container 就不會啟動
    • native Sidecar 的啟動與停止規則則如前一節所述。
  4. Init Container 全部成功後,App Container 開始執行。
    • 多個一般 container 之間沒有辦法保證啟動先後順序。
  5. Pod 在服務期間持續回報狀態;其中的 App Container 與 Sidecar 依各自的 lifecycle 運作。
  6. Pod 被刪除時會進入終止流程。
    • 若 Pod 是由 Deployment 管理,當 Node 無法使用、Kubernetes 偵測到該 Node 上的 Pod 已無法正常服務後,為了維持原先宣告的 Pod 數量,Controller 會建立新的 Pod,接著 Scheduler 會為新 Pod 選擇可用的 Node。
    • 舊 Pod 的狀態不一定能立即被正常回收,而該 Pod 也不會原地復活(整顆故障)。

了解 Pod phase、container state 與 kubectl STATUS

在實際操作 K8s 時,可以透過 kubectl get podkubectl describe pod 來觀察 Pod 的狀態,而這兩種指令顯示的狀態內容會有以下三種內容:

層次 狀態 用意
Pod phase PendingRunningSucceededFailedUnknown Pod 整體生命週期的摘要狀態。Running 不代表應用已經 Ready。
container state WaitingRunningTerminated Pod 裡每一個 App Container、Init Container 或 Sidecar 各自的執行狀態。
kubectl 的 STATUS 顯示 Init:ImagePullBackOffCrashLoopBackOffTerminating 為了方便人閱讀而彙整出的提示;不能直接當成 Pod phase。

例如 phase: Running 只表示 Pod 已被指派到 Node、container 已建立,且至少有一個 container 正在執行或重啟中;它不代表所有 container 都正常,也不代表 Pod 已經 Ready、可以接收流量。

Init:ImagePullBackOff 表示 initContainers 中某個 container 的 image 無法拉取。

CrashLoopBackOff 則表示某個 container 反覆失敗、重啟等待時間逐漸拉長。兩者都是排查的重要線索,但不是 Pod phase。

因此看到 Pod 異常時,除了 kubectl get pod,也應使用 kubectl describe pod <pod-name> 查看 Event,並確認 initContainerStatusescontainerStatuses 中究竟是哪一個 container 出錯。

經驗分享:Pod 內有多個 Container,讓我誤判問題

分享個真實經驗,曾經有一組 Pod 顯示 Init:ImagePullBackOff,一開始直覺會以為是 AP 程式的 image、Pod 網路,甚至 CoreDNS 出了問題,但詳細看 Event 與 initContainerStatuses 後,才發現失敗的是 Service Mesh 自動注入的 container image,而不是 AP 程式的 image。

由於 Pod 顯示的是 Init:ImagePullBackOff,實際排查時還要確認失敗的 container 名稱,以及它位於 initContainers 還是 containers

更特別的是,只有一台 Worker Node 會失敗。該 Node 的 Runtime 要拉取新 image 時,使用的 Node DNS resolver 發生 timeout,而 AP 程式 image 因為已經存在 local cache,所以看起來像「應用正常,只有奇怪的 container 壞掉」。

這個案例提醒我:看到 Pod 啟動失敗時,先用 kubectl describe pod 看 Event 與每個 container 的狀態,確認到底是 App Container、Init Container、Sidecar,還是 Node 層的 Runtime 問題。

在 Pod 擁有多個 container 的情況下,每個 container 都可能出現問題。

今日結論

Pod 是 Kubernetes 真正部署的最小單位,它把一個或多個需要緊密合作的 container 放在同一個環境中。

Init Container 負責啟動前的工作,App Container 提供服務,Sidecar 則在旁協助運作。

Sidecar 可以是與 App Container 同層的傳統 Sidecar,也可以是具有順序的 native Sidecar。

參考資料


上一篇
Day 7 - CRI、CNI、CSI 三種介面與用途
下一篇
Day 9 - Deployment 與 Replica
系列文
初見 Kubernetes - 純網站後端開發踏入 K8s 世界的經驗分享9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言